package com.rexpress.xposed.malwarebuster;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.Filter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SearchView;
import android.widget.SectionIndexer;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;


public class MainActivity extends Activity {

    private ArrayList<ApplicationInfo> appList = new ArrayList<ApplicationInfo>();
    private ArrayList<ApplicationInfo> filteredAppList = new ArrayList<ApplicationInfo>();
    private SharedPreferences prefs;

    private String nameFilter;
    private FilterState filterAppType;
    private FilterState filterAppState;
    private FilterState filterActive;
    private String filterPermissionUsage;

    /**
     * Possible values for the filter state: All / Overridden / Unchanged
     */
    public static enum FilterState {
        ALL, OVERRIDDEN, UNCHANGED;
    }

    private static boolean isModActive() {
        return false;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (!isModActive()) {
            alertXposedInstaller();
        }

        prefs = getSharedPreferences(Common.PREFS, MODE_WORLD_READABLE);

        ListView list = (ListView) findViewById(R.id.lstApps);
        list.setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
				/*
				// Open settings activity when clicking on an application
				String pkgName = ((TextView) view.findViewById(R.id.app_package)).getText().toString();
				Intent i = new Intent(getApplicationContext(), ApplicationSettings.class);
				i.putExtra("package", pkgName);
				startActivityForResult(i, position);
                */

                CheckBox checkBox = (CheckBox) view.findViewById(R.id.app_checkBox);

                String pkgName = ((TextView) view.findViewById(R.id.app_package)).getText().toString();

                boolean isChecked = checkBox.isChecked();

                SharedPreferences.Editor prefsEditor = prefs.edit();
                prefsEditor.putBoolean(pkgName, !isChecked);
                prefsEditor.commit();

                Toast.makeText(MainActivity.this, pkgName, Toast.LENGTH_LONG).show();
                checkBox.setChecked(!isChecked);

            }
        });

        ((ImageButton) findViewById(R.id.btnGlobalHook)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean oldStatus = prefs.getBoolean(Common.GLOBALHOOK, false);
                boolean newStatus = !oldStatus;

                SharedPreferences.Editor prefsEditor = prefs.edit();
                prefsEditor.putBoolean(Common.GLOBALHOOK, newStatus);
                prefsEditor.commit();

                alertGlobalHook(newStatus);

                refreshGlobalHookControls();

                //Toast.makeText(MainActivity.this, "hi", Toast.LENGTH_LONG).show();
            }
        });

        refreshGlobalHookControls();

        refreshApps();
    }

    private boolean appInstalledOrNot(String uri) {
        PackageManager pm = getPackageManager();
        boolean app_installed = false;
        try {
            pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
            app_installed = true;
        }
        catch (PackageManager.NameNotFoundException e) {
            app_installed = false;
        }
        return app_installed ;
    }

    private void alertXposedInstaller() {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);

        builder.setTitle(R.string.alertWarning);

        if (appInstalledOrNot(Common.XPOSEDINSTALLER_PACKAGE_NAME)) {
            builder.setMessage(R.string.alertXposedModuleNotActive);
            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                    Intent intent = new Intent(Common.XPOSEDINSTALLER_PACKAGE_NAME + ".OPEN_SECTION");
                    intent.setPackage(Common.XPOSEDINSTALLER_PACKAGE_NAME);
                    intent.putExtra("section", "modules");
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                }
            });
        }
        else {
            builder.setMessage(R.string.alertXposedModuleNotInstalled);
            builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=" + Common.XPOSEDINSTALLER_PACKAGE_NAME)));
                }
            });
        }

        builder.show();
    }

    private void alertGlobalHook(boolean newStatus) {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);

        builder.setMessage(newStatus?R.string.globalWarningText:R.string.alertGlobalhookDisabled);
        builder.setTitle(R.string.alertWarning);

        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }
        });

        builder.create().show();
    }

    private void refreshGlobalHookControls() {
        boolean currentStatus = prefs.getBoolean(Common.GLOBALHOOK, false);

        ImageButton glbHookbtn = (ImageButton)findViewById(R.id.btnGlobalHook);
        ListView list = (ListView) findViewById(R.id.lstApps);
        TextView globalWarningTextView = (TextView) findViewById(R.id.globalWarningTextView);
        SearchView searchApp = (SearchView) findViewById(R.id.searchApp);

        if (currentStatus) {
            glbHookbtn.setBackgroundColor(0xff34bcf1);
            list.setVisibility(View.GONE);
            globalWarningTextView.setVisibility(View.VISIBLE);
            searchApp.setEnabled(false);
        }
        else {
            glbHookbtn.setBackgroundColor(Color.DKGRAY);
            list.setVisibility(View.VISIBLE);
            globalWarningTextView.setVisibility(View.GONE);
            searchApp.setEnabled(true);
        }
    }

    private void refreshApps() {
        appList.clear();
        // (re)load the list of apps in the background
        new PrepareAppsAdapter().execute();
    }


    private class AppListFilter extends Filter {

        private AppListAdapter adapter;

        AppListFilter(AppListAdapter adapter) {
            super();
            this.adapter = adapter;
        }

        @Override
        protected FilterResults performFiltering(CharSequence constraint) {
            // NOTE: this function is *always* called from a background thread, and
            // not the UI thread.

            ArrayList<ApplicationInfo> items = new ArrayList<ApplicationInfo>();
            synchronized (this) {
                items.addAll(appList);
            }

            SharedPreferences prefs = getSharedPreferences(Common.PREFS, Context.MODE_WORLD_READABLE);

            FilterResults result = new FilterResults();
            if (constraint != null && constraint.length() > 0) {
                Pattern regexp = Pattern.compile(constraint.toString(), Pattern.LITERAL | Pattern.CASE_INSENSITIVE);
                for (Iterator<ApplicationInfo> i = items.iterator(); i.hasNext(); ) {
                    ApplicationInfo app = i.next();
                    if (!regexp.matcher(app.name == null ? "" : app.name).find()
                            && !regexp.matcher(app.packageName).find()) {
                        i.remove();
                    }
                }
            }
            for (Iterator<ApplicationInfo> i = items.iterator(); i.hasNext(); ) {
                ApplicationInfo app = i.next();
                if (filteredOut(prefs, app))
                    i.remove();
            }

            result.values = items;
            result.count = items.size();

            return result;
        }

        private boolean filteredOut(SharedPreferences prefs, ApplicationInfo app) {
            String packageName = app.packageName;
            boolean isUser = (app.flags & ApplicationInfo.FLAG_SYSTEM) == 0;
/*
            // AppType = Overridden is used for USER apps
            if (filteredOut(isUser, filterAppType))
                return true;

            // AppState = Overridden is used for ENABLED apps
            if (filteredOut(app.enabled, filterAppState))
                return true;

            if (filteredOut(prefs.getBoolean(packageName + Common.PREF_ACTIVE, false), filterActive))
                return true;

            if (FilterState.UNCHANGED.equals(filterActive))
                // Ignore additional filters
                return false;

            for (SettingInfo setting : settings)
                if (filteredOut(prefs.contains(packageName + setting.settingKey), setting.filter))
                    return true;

            if (filterPermissionUsage != null) {
                Set<String> pkgsForPerm = permUsage.get(filterPermissionUsage);
                if (!pkgsForPerm.contains(packageName))
                    return true;
            }
*/
            return false;
        }

        private boolean filteredOut(boolean set, FilterState state) {
            if (state == null)
                return false;

            switch (state) {
                case UNCHANGED:
                    return set;
                case OVERRIDDEN:
                    return !set;
                default:
                    return false;
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        protected void publishResults(CharSequence constraint, FilterResults results) {
            // NOTE: this function is *always* called from the UI thread.
            filteredAppList = (ArrayList<ApplicationInfo>) results.values;
            adapter.notifyDataSetChanged();
            adapter.clear();
            for (int i = 0, l = filteredAppList.size(); i < l; i++) {
                adapter.add(filteredAppList.get(i));
            }
            adapter.notifyDataSetInvalidated();
        }
    }

    static class AppListViewHolder {
        TextView app_name;
        TextView app_package;
        ImageView app_icon;
        CheckBox app_checkBox;

        AsyncTask<AppListViewHolder, Void, Drawable> imageLoader;
    }

    class AppListAdapter extends ArrayAdapter<ApplicationInfo> implements SectionIndexer {

        private Map<String, Integer> alphaIndexer;
        private String[] sections;
        private Filter filter;
        private LayoutInflater inflater;
        private Drawable defaultIcon;

        @SuppressLint("DefaultLocale")
        public AppListAdapter(Context context, List<ApplicationInfo> items) {
            super(context, R.layout.app_list_item, new ArrayList<ApplicationInfo>(items));

            filteredAppList.addAll(items);

            filter = new AppListFilter(this);
            inflater = getLayoutInflater();
            defaultIcon = getResources().getDrawable(android.R.drawable.sym_def_app_icon);

            alphaIndexer = new HashMap<String, Integer>();
            for (int i = filteredAppList.size() - 1; i >= 0; i--) {
                ApplicationInfo app = filteredAppList.get(i);
                String appName = app.name;
                String firstChar;
                if (appName == null || appName.length() < 1) {
                    firstChar = "@";
                } else {
                    firstChar = appName.substring(0, 1).toUpperCase();
                    if (firstChar.charAt(0) > 'Z' || firstChar.charAt(0) < 'A')
                        firstChar = "@";
                }

                alphaIndexer.put(firstChar, i);
            }

            Set<String> sectionLetters = alphaIndexer.keySet();

            // create a list from the set to sort
            List<String> sectionList = new ArrayList<String>(sectionLetters);

            Collections.sort(sectionList);

            sections = new String[sectionList.size()];

            sectionList.toArray(sections);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            // Load or reuse the view for this row
            View row = convertView;
            AppListViewHolder holder;
            if (row == null) {
                row = inflater.inflate(R.layout.app_list_item, parent, false);
                holder = new AppListViewHolder();
                holder.app_name = (TextView) row.findViewById(R.id.app_name);
                holder.app_package = (TextView) row.findViewById(R.id.app_package);
                holder.app_icon = (ImageView) row.findViewById(R.id.app_icon);
                holder.app_checkBox = (CheckBox) row.findViewById(R.id.app_checkBox);
                row.setTag(holder);
            } else {
                holder = (AppListViewHolder) row.getTag();
                holder.imageLoader.cancel(true);
            }

            final ApplicationInfo app = filteredAppList.get(position);

            holder.app_name.setText(app.name == null ? "" : app.name);
            //holder.app_package.setTextColor(prefs.getBoolean(app.packageName + Common.PREF_ACTIVE, false)
            //        ? Color.RED : Color.parseColor("#0099CC"));
            holder.app_package.setText(app.packageName);
            holder.app_icon.setImageDrawable(defaultIcon);

            boolean isChecked = app.name == null ? false : prefs.getBoolean(app.packageName, false);
            holder.app_checkBox.setChecked(isChecked);

            if (app.enabled) {
                holder.app_name.setPaintFlags(holder.app_name.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
                holder.app_package.setPaintFlags(holder.app_package.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
            } else {
                holder.app_name.setPaintFlags(holder.app_name.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
                holder.app_package.setPaintFlags(holder.app_package.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
            }

            holder.imageLoader = new AsyncTask<AppListViewHolder, Void, Drawable>() {
                private AppListViewHolder v;

                @Override
                protected Drawable doInBackground(AppListViewHolder... params) {
                    v = params[0];
                    return app.loadIcon(getPackageManager());
                }

                @Override
                protected void onPostExecute(Drawable result) {
                    v.app_icon.setImageDrawable(result);
                }
            }.execute(holder);

            return row;
        }

        @SuppressLint("DefaultLocale")
        @Override
        public void notifyDataSetInvalidated() {
            alphaIndexer.clear();
            for (int i = filteredAppList.size() - 1; i >= 0; i--) {
                ApplicationInfo app = filteredAppList.get(i);
                String appName = app.name;
                String firstChar;
                if (appName == null || appName.length() < 1) {
                    firstChar = "@";
                } else {
                    firstChar = appName.substring(0, 1).toUpperCase();
                    if (firstChar.charAt(0) > 'Z' || firstChar.charAt(0) < 'A')
                        firstChar = "@";
                }
                alphaIndexer.put(firstChar, i);
            }

            Set<String> keys = alphaIndexer.keySet();
            Iterator<String> it = keys.iterator();
            ArrayList<String> keyList = new ArrayList<String>();
            while (it.hasNext()) {
                keyList.add(it.next());
            }

            Collections.sort(keyList);
            sections = new String[keyList.size()];
            keyList.toArray(sections);

            super.notifyDataSetInvalidated();
        }

        @Override
        public int getPositionForSection(int section) {
            if (section >= sections.length)
                return filteredAppList.size() - 1;

            return alphaIndexer.get(sections[section]);
        }

        @Override
        public int getSectionForPosition(int position) {

            // Iterate over the sections to find the closest index
            // that is not greater than the position
            int closestIndex = 0;
            int latestDelta = Integer.MAX_VALUE;

            for (int i = 0; i < sections.length; i++) {
                int current = alphaIndexer.get(sections[i]);
                if (current == position) {
                    // If position matches an index, return it immediately
                    return i;
                } else if (current < position) {
                    // Check if this is closer than the last index we inspected
                    int delta = position - current;
                    if (delta < latestDelta) {
                        closestIndex = i;
                        latestDelta = delta;
                    }
                }
            }

            return closestIndex;
        }

        @Override
        public Object[] getSections() {
            return sections;
        }

        @Override
        public Filter getFilter() {
            return filter;
        }
    }

    // Handle background loading of apps
    private class PrepareAppsAdapter extends AsyncTask<Void,Void,AppListAdapter> {
        ProgressDialog dialog;

        @Override
        protected void onPreExecute() {
            dialog = new ProgressDialog(((ListView) findViewById(R.id.lstApps)).getContext());
            dialog.setMessage(getString(R.string.app_loading));
            dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
            dialog.setCancelable(false);
            dialog.show();
        }

        @Override
        protected AppListAdapter doInBackground(Void... params) {
            if (appList.size() == 0) {
                loadApps(dialog);
            }
            return null;
        }

        @Override
        protected void onPostExecute(final AppListAdapter result) {
            prepareAppList();

            try {
                dialog.dismiss();
            } catch (Exception e) {

            }
        }
    }

    @SuppressLint("DefaultLocale")
    private void loadApps(ProgressDialog dialog) {

        appList.clear();

        PackageManager pm = getPackageManager();
        List<PackageInfo> pkgs = getPackageManager().getInstalledPackages(PackageManager.GET_META_DATA);
        dialog.setMax(pkgs.size());
        int i = 1;
        for (PackageInfo pkgInfo : pkgs) {
            dialog.setProgress(i++);

            if ((pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 1)
                continue;

            ApplicationInfo appInfo = pkgInfo.applicationInfo;
            if (appInfo == null)
                continue;

            appInfo.name = appInfo.loadLabel(pm).toString();
            appList.add(appInfo);
        }

        Collections.sort(appList, new Comparator<ApplicationInfo>() {
            @Override
            public int compare(ApplicationInfo lhs, ApplicationInfo rhs) {
                if (lhs.name == null) {
                    return -1;
                } else if (rhs.name == null) {
                    return 1;
                } else {
                    return lhs.name.toUpperCase().compareTo(rhs.name.toUpperCase());
                }
            }
        });
    }

    private void prepareAppList() {
        final AppListAdapter appListAdapter = new AppListAdapter(MainActivity.this, appList);

        ((ListView) findViewById(R.id.lstApps)).setAdapter(appListAdapter);
        //appListAdapter.getFilter().filter(nameFilter);
        ((SearchView) findViewById(R.id.searchApp)).setOnQueryTextListener(new SearchView.OnQueryTextListener() {

            @Override
            public boolean onQueryTextSubmit(String query) {
                nameFilter = query;
                appListAdapter.getFilter().filter(nameFilter);
                ((SearchView) findViewById(R.id.searchApp)).clearFocus();
                return false;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                nameFilter = newText;
                appListAdapter.getFilter().filter(nameFilter);
                return false;
            }

        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        switch (item.getItemId()) {
        case R.id.menu_refresh:
            refreshApps();
            return true;
        case R.id.menu_help:
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/twilightgod/MalwareBuster/wiki")));
            return true;
        case R.id.menu_about:
            showAboutDialog();
            return true;
        default:
        return super.onOptionsItemSelected(item);
        }
    }

    private void showAboutDialog() {
        View vAbout;
        vAbout = getLayoutInflater().inflate(R.layout.about, null);

        // Warn if the module is not active
        //if (!isModActive())
          //  vAbout.findViewById(R.id.about_notactive).setVisibility(View.VISIBLE);

        // Display the resources translator, or hide it if none
        //String translator = getResources().getString(R.string.translator);
        //TextView txtTranslation = (TextView) vAbout.findViewById(R.id.about_translation);
       // if (translator.isEmpty()) {
         //   txtTranslation.setVisibility(View.GONE);
        //} else {
         //   txtTranslation.setText(getString(R.string.app_translation, translator));
          //  txtTranslation.setMovementMethod(LinkMovementMethod.getInstance());
        //}

        // Clickable links
        ((TextView) vAbout.findViewById(R.id.about_title)).setMovementMethod(LinkMovementMethod.getInstance());

        // Display the correct version
        try {
            ((TextView) vAbout.findViewById(R.id.version)).setText(getString(R.string.app_version,
                    getPackageManager().getPackageInfo(getPackageName(), 0).versionName));
        } catch (PackageManager.NameNotFoundException e) {
        }

        // Prepare and show the dialog
        AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(this);
        dlgBuilder.setTitle(R.string.app_name);
        dlgBuilder.setCancelable(true);
        dlgBuilder.setIcon(R.drawable.ic_launcher);
        dlgBuilder.setPositiveButton(android.R.string.ok, null);
        dlgBuilder.setView(vAbout);
        dlgBuilder.show();
    }

}
